#include <linux/err.h>
#include <linux/fs2.h>
#include <linux/sched.h>
#include <linux/fs_struct.h>
#include <linux/fcntl.h>

char * getname(const char * filename)
{
	// char *tmp, *result;

	// result = ERR_PTR(-ENOMEM);
	// tmp = __getname();
	// if (tmp)  {
	// 	int retval = do_getname(filename, tmp);

	// 	result = tmp;
	// 	if (retval < 0) {
	// 		__putname(tmp);
	// 		result = ERR_PTR(retval);
	// 	}
	// }
	// audit_getname(result);

	char *result = kmalloc(4096);

	strcpy(result, filename, strlen(filename));

	return result;
}

static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// dput(nd->path.dentry);
	// if (nd->path.mnt != path->mnt)
		// mntput(nd->path.mnt);
	nd->path.mnt = path->mnt;
	nd->path.dentry = path->dentry;
}

static void set_root(struct nameidata *nd)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!nd->root.mnt) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);

		struct fs_struct *fs = current->fs;
		// read_lock(&fs->lock);
		nd->root = fs->root;
		// path_get(&nd->root);
		// read_unlock(&fs->lock);
	}
}

static struct dentry *real_lookup(struct dentry *parent, struct qstr *name, struct nameidata *nd)
{
	struct dentry *result;
	struct inode *dir = parent->d_inode;

	// mutex_lock(&dir->i_mutex);
	/*
	 * First re-do the cached lookup just in case it was created
	 * while we waited for the directory semaphore..
	 *
	 * FIXME! This could use version numbering or similar to
	 * avoid unnecessary cache lookups.
	 *
	 * The "dcache_lock" is purely to protect the RCU list walker
	 * from concurrent renames at this point (we mustn't get false
	 * negatives from the RCU list walk here, unlike the optimistic
	 * fast walk).
	 *
	 * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
	 */
	result = d_lookup(parent, name);
	if (!result) {
		struct dentry *dentry;

		/* Don't create child dentry for a dead directory. */
		// result = ERR_PTR(-ENOENT);
		// if (IS_DEADDIR(dir))
		// 	goto out_unlock;

		dentry = d_alloc(parent, name);
		result = ERR_PTR(-ENOMEM);
		if (dentry) {
			result = dir->i_op->lookup(dir, dentry, nd);
			if (result) {
				printf("this is %s(): %d\r\n", __func__, __LINE__);
				// dput(dentry);
			} else {
				printf("this is %s(): %d\r\n", __func__, __LINE__);
				result = dentry;
			}
		}
// out_unlock:
		// mutex_unlock(&dir->i_mutex);
		return result;
	}

	/*
	 * Uhhuh! Nasty case: the cache was re-populated while
	 * we waited on the semaphore. Need to revalidate.
	 */
	// mutex_unlock(&dir->i_mutex);
	// if (result->d_op && result->d_op->d_revalidate) {
	// 	result = do_revalidate(result, nd);
	// 	if (!result)
	// 		result = ERR_PTR(-ENOENT);
	// }
	// return result;
}

static int __follow_mount(struct path *path)
{
	int res = 0;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	while (d_mountpoint(path->dentry)) {
		printf("this is %s(): %d >>> path->dentry->d_name.name = %s\r\n", __func__, __LINE__, path->dentry->d_name.name);
		struct vfsmount *mounted = lookup_mnt(path);
		if (!mounted)
			break;
		printf("this is %s(): %d >>> mounted->mnt_sb->s_type->name = %s\r\n", __func__, __LINE__, mounted->mnt_sb->s_type->name);
		printf("this is %s(): %d >>> mounted->mnt_root->d_name.name = %s\r\n", __func__, __LINE__, mounted->mnt_root->d_name.name);
		printf("this is %s(): %d >>> mounted->mnt_mountpoint->d_name.name = %s\r\n", __func__, __LINE__, mounted->mnt_mountpoint->d_name.name);
		// dput(path->dentry);
		// if (res)
		// 	mntput(path->mnt);
		path->mnt = mounted;
		path->dentry = dget(mounted->mnt_root);
		res = 1;
		printf("this is %s(): %d\r\n", __func__, __LINE__);
	}
	return res;
}

static int do_lookup(struct nameidata *nd, struct qstr *name, struct path *path)
{
	printf("this is %s(): %d >>> name->name = %s\r\n", __func__, __LINE__, name->name);
	struct vfsmount *mnt = nd->path.mnt;
	struct dentry *dentry = __d_lookup(nd->path.dentry, name);

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!dentry)
		goto need_lookup;
	printf("this is %s(): %d >>> dentry->d_name.name = %s\r\n", __func__, __LINE__, dentry->d_name.name);
	// if (dentry->d_op && dentry->d_op->d_revalidate)
	// 	goto need_revalidate;
done:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	path->mnt = mnt;
	path->dentry = dentry;
	__follow_mount(path);
	return 0;

need_lookup:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	dentry = real_lookup(nd->path.dentry, name, nd);
	if (IS_ERR(dentry))
		goto fail;
	goto done;

// need_revalidate:
// 	dentry = do_revalidate(dentry, nd);
// 	if (!dentry)
// 		goto need_lookup;
// 	if (IS_ERR(dentry))
// 		goto fail;
// 	goto done;

fail:
	return PTR_ERR(dentry);
}

static int __link_path_walk(const char *name, struct nameidata *nd)
{
	struct path next;
	struct inode *inode;
	int err;
	unsigned int lookup_flags = nd->flags;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	while (*name=='/')
		name++;
	if (!*name)
		goto return_reval;

	inode = nd->path.dentry->d_inode;
	// if (nd->depth)
	// 	lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);

	/* At this point we know we have a real path component. */
	for(;;) {
		unsigned long hash;
		struct qstr this;
		unsigned int c;

		// nd->flags |= LOOKUP_CONTINUE;
		// err = exec_permission_lite(inode);
 		// if (err)
		// 	break;

		this.name = name;
		printf("this is %s(): %d >>> this.name = %s\r\n", __func__, __LINE__, this.name);
		c = *(const unsigned char *)name;
		printf("this is %s(): %d >>> c = %c\r\n", __func__, __LINE__, c);

		hash = init_name_hash();
		do {
			name++;
			hash = partial_name_hash(c, hash);
			c = *(const unsigned char *)name;
			printf("this is %s(): %d >>> c = %c\r\n", __func__, __LINE__, c);
		} while (c && (c != '/'));
		this.len = name - (const char *) this.name;
		this.hash = end_name_hash(hash);

		printf("this is %s(): %d\r\n", __func__, __LINE__);

		/* remove trailing slashes? */
		if (!c)
			goto last_component;
		while (*++name == '/');
		if (!*name)
			goto last_with_slashes;

		/*
		 * "." and ".." are special - ".." especially so because it has
		 * to be able to know about the current root directory and
		 * parent relationships.
		 */
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		if (this.name[0] == '.')
			switch (this.len) {
				default:
					printf("this is %s(): %d\r\n", __func__, __LINE__);
					break;
				case 2:	
					printf("this is %s(): %d\r\n", __func__, __LINE__);
					if (this.name[1] != '.')
						break;
					// follow_dotdot(nd);
					inode = nd->path.dentry->d_inode;
					/* fallthrough */
				case 1:
					printf("this is %s(): %d\r\n", __func__, __LINE__);
					continue;
			}
		/*
		 * See if the low-level filesystem might want
		 * to use its own hash..
		 */
		if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
			printf("this is %s(): %d\r\n", __func__, __LINE__);
			err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
							    &this);
			if (err < 0)
				break;
			printf("this is %s(): %d\r\n", __func__, __LINE__);
		}
		/* This does the actual lookups.. */
		err = do_lookup(nd, &this, &next);
		if (err)
			break;

		err = -ENOENT;
		inode = next.dentry->d_inode;
		if (!inode)
			goto out_dput;

		// if (inode->i_op->follow_link) {
		// 	err = do_follow_link(&next, nd);
		// 	if (err)
		// 		goto return_err;
		// 	err = -ENOENT;
		// 	inode = nd->path.dentry->d_inode;
		// 	if (!inode)
		// 		break;
		// } else
			path_to_nameidata(&next, nd);
		err = -ENOTDIR; 
		// if (!inode->i_op->lookup)
		// 	break;
		continue;
		/* here ends the main loop */

last_with_slashes:
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		// lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
last_component:
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		/* Clear LOOKUP_CONTINUE iff it was previously unset */
		// nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
		if (lookup_flags & LOOKUP_PARENT)
			goto lookup_parent;
		// if (this.name[0] == '.') switch (this.len) {
		// 	default:
		// 		break;
		// 	case 2:	
		// 		if (this.name[1] != '.')
		// 			break;
		// 		follow_dotdot(nd);
		// 		inode = nd->path.dentry->d_inode;
		// 		/* fallthrough */
		// 	case 1:
		// 		goto return_reval;
		// }
		// if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
		// 	err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
		// 					    &this);
		// 	if (err < 0)
		// 		break;
		// }
		err = do_lookup(nd, &this, &next);
		if (err)
			break;
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		inode = next.dentry->d_inode;
		// if ((lookup_flags & LOOKUP_FOLLOW)
		//     && inode && inode->i_op->follow_link) {
		// 	err = do_follow_link(&next, nd);
		// 	if (err)
		// 		goto return_err;
		// 	inode = nd->path.dentry->d_inode;
		// } else
			path_to_nameidata(&next, nd);
		err = -ENOENT;
		if (!inode)
			break;
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		if (lookup_flags & LOOKUP_DIRECTORY) {
			printf("this is %s(): %d\r\n", __func__, __LINE__);
			err = -ENOTDIR; 
			if (!inode->i_op->lookup)
				break;
			printf("this is %s(): %d\r\n", __func__, __LINE__);
		}
		goto return_base;
lookup_parent:
		nd->last = this;
		nd->last_type = LAST_NORM;
		if (this.name[0] != '.')
			goto return_base;
		if (this.len == 1)
			nd->last_type = LAST_DOT;
		else if (this.len == 2 && this.name[1] == '.')
			nd->last_type = LAST_DOTDOT;
		else
			goto return_base;
return_reval:
		/*
		 * We bypassed the ordinary revalidation routines.
		 * We may need to check the cached dentry for staleness.
		 */
		// if (nd->path.dentry && nd->path.dentry->d_sb &&
		//     (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) {
		// 	err = -ESTALE;
		// 	/* Note: we do not d_invalidate() */
		// 	if (!nd->path.dentry->d_op->d_revalidate(
		// 			nd->path.dentry, nd))
		// 		break;
		// }
return_base:
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		return 0;
out_dput:
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		// path_put_conditional(&next, nd);
		break;
	}
	// path_put(&nd->path);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
return_err:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return err;
}

static int link_path_walk(const char *name, struct nameidata *nd)
{
	// struct path save = nd->path;
	int result;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	/* make sure the stuff we saved doesn't go away */
	// path_get(&save);

	result = __link_path_walk(name, nd);
	// if (result == -ESTALE) {
	// 	/* nd->path had been dropped */
	// 	nd->path = save;
	// 	path_get(&nd->path);
	// 	nd->flags |= LOOKUP_REVAL;
	// 	result = __link_path_walk(name, nd);
	// }

	// path_put(&save);

	return result;
}

static int path_walk(const char *name, struct nameidata *nd)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// current->total_link_count = 0;
	return link_path_walk(name, nd);
}

static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
{
	int retval = 0;
	int fput_needed;
	struct file *file;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// nd->last_type = LAST_ROOT; /* if there are only slashes... */
	nd->flags = flags;
	nd->depth = 0;
	nd->root.mnt = NULL;

	if (*name=='/') {
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		set_root(nd);
		nd->path = nd->root;
		// path_get(&nd->root);
	} else if (dfd == AT_FDCWD) {
		struct fs_struct *fs = current->fs;
		// read_lock(&fs->lock);
		nd->path = fs->pwd;
		// path_get(&fs->pwd);
		// read_unlock(&fs->lock);
	} else {
		struct dentry *dentry;

		file = fget_light(dfd, &fput_needed);
		retval = -EBADF;
		if (!file)
			goto out_fail;

		dentry = file->f_path.dentry;

		// retval = -ENOTDIR;
		// if (!S_ISDIR(dentry->d_inode->i_mode))
		// 	goto fput_fail;

		// retval = file_permission(file, MAY_EXEC);
		// if (retval)
		// 	goto fput_fail;

		nd->path = file->f_path;
		// path_get(&file->f_path);

		// fput_light(file, fput_needed);
	}
	return 0;

fput_fail:
	// fput_light(file, fput_needed);
out_fail:
	return retval;
}

static int do_path_lookup(int dfd, const char *name,
				unsigned int flags, struct nameidata *nd)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	int retval = path_init(dfd, name, flags, nd);
	if (!retval)
		retval = path_walk(name, nd);
	// if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
	// 			nd->path.dentry->d_inode))
	// 	audit_inode(name, nd->path.dentry);
	if (nd->root.mnt) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		// path_put(&nd->root);
		nd->root.mnt = NULL;
	}
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return retval;
}

int kern_path(const char *name, unsigned int flags, struct path *path)
{
	struct nameidata nd;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	int res = do_path_lookup(AT_FDCWD, name, flags, &nd);
	if (!res)
		*path = nd.path;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return res;
}

static int path_lookup_open(int dfd, const char *name,
		unsigned int lookup_flags, struct nameidata *nd, int open_flags)
{
	struct file *filp = get_empty_filp();
	int err;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (filp == NULL)
		return -ENFILE;
	nd->intent.open.file = filp;
	nd->intent.open.flags = open_flags;
	nd->intent.open.create_mode = 0;
	// err = do_path_lookup(dfd, name, lookup_flags|LOOKUP_OPEN, nd);
	err = do_path_lookup(dfd, name, lookup_flags, nd);
	// if (IS_ERR(nd->intent.open.file)) {
	// 	if (err == 0) {
	// 		err = PTR_ERR(nd->intent.open.file);
	// 		path_put(&nd->path);
	// 	}
	// } else if (err != 0)
	// 	release_open_intent(nd);
	return err;
}

int user_path_at(int dfd, const char *name, unsigned flags,
		 struct path *path)
{
	struct nameidata nd;
	char *tmp = getname(name);
	int err = PTR_ERR(tmp);

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (!IS_ERR(tmp)) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);

		// BUG_ON(flags & LOOKUP_PARENT);

		err = do_path_lookup(dfd, tmp, flags, &nd);
		// putname(tmp);
		if (!err)
			*path = nd.path;
		printf("this is %s(): %d\r\n", __func__, __LINE__);
	}
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return err;
}

int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	int error;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// int error = may_create(dir, dentry);

	// if (error)
	// 	return error;

	if (!dir->i_op->create)
		return -EACCES;	/* shouldn't it be ENOSYS? */
	// mode &= S_IALLUGO;
	// mode |= S_IFREG;
	// error = security_inode_create(dir, dentry, mode);
	// if (error)
	// 	return error;
	// vfs_dq_init(dir);
	error = dir->i_op->create(dir, dentry, mode, nd);
	// if (!error)
	// 	fsnotify_create(dir, dentry);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return error;
}

static int __open_namei_create(struct nameidata *nd, struct path *path,
				int flag, int mode)
{
	int error;
	struct dentry *dir = nd->path.dentry;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// if (!IS_POSIXACL(dir->d_inode))
	// 	mode &= ~current_umask();
	// error = security_path_mknod(&nd->path, path->dentry, mode, 0);
	// if (error)
	// 	goto out_unlock;
	error = vfs_create(dir->d_inode, path->dentry, mode, nd);
out_unlock:
	// mutex_unlock(&dir->d_inode->i_mutex);
	// dput(nd->path.dentry);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	nd->path.dentry = path->dentry;
	if (error)
		return error;
	/* Don't check for write permission, don't truncate */
	// return may_open(&nd->path, 0, flag & ~O_TRUNC);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return error;
}

static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
{
	struct dentry * dentry = __d_lookup(parent, name);

	/* lockess __d_lookup may fail due to concurrent d_move() 
	 * in some unrelated directory, so try with d_lookup
	 */
	if (!dentry)
		dentry = d_lookup(parent, name);

	// if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
	// 	dentry = do_revalidate(dentry, nd);

	return dentry;
}

static struct dentry *__lookup_hash(struct qstr *name,
		struct dentry *base, struct nameidata *nd)
{
	struct dentry *dentry;
	struct inode *inode;
	int err;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	inode = base->d_inode;

	/*
	 * See if the low-level filesystem might want
	 * to use its own hash..
	 */
	// if (base->d_op && base->d_op->d_hash) {
	// 	err = base->d_op->d_hash(base, name);
	// 	dentry = ERR_PTR(err);
	// 	if (err < 0)
	// 		goto out;
	// }

	dentry = cached_lookup(base, name, nd);
	if (!dentry) {
		struct dentry *new;

		/* Don't create child dentry for a dead directory. */
		dentry = ERR_PTR(-ENOENT);
		// if (IS_DEADDIR(inode))
		// 	goto out;

		new = d_alloc(base, name);
		dentry = ERR_PTR(-ENOMEM);
		if (!new)
			goto out;
		dentry = inode->i_op->lookup(inode, new, nd);
		if (!dentry)
			dentry = new;
		// else
			// dput(new);
	}
out:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return dentry;
}

static struct dentry *lookup_hash(struct nameidata *nd)
{
	int err;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
	// if (err)
	// 	return ERR_PTR(err);
	return __lookup_hash(&nd->last, nd->path.dentry, nd);
}

struct file *do_filp_open(int dfd, const char *pathname,
		int open_flag, int mode, int acc_mode)
{
	struct file *filp;
	struct nameidata nd;
	int error;
	struct path path;
	struct dentry *dir;
	int count = 0;
	int will_write;
	// int flag = open_to_namei_flags(open_flag);
	int flag = open_flag;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// if (!acc_mode)
	// 	acc_mode = MAY_OPEN | ACC_MODE(flag);

	// /* O_TRUNC implies we need access checks for write permissions */
	// if (flag & O_TRUNC)
	// 	acc_mode |= MAY_WRITE;

	// /* Allow the LSM permission hook to distinguish append 
	//    access from general write access. */
	// if (flag & O_APPEND)
	// 	acc_mode |= MAY_APPEND;

	/*
	 * The simplest case - just a plain lookup.
	 */
	if (!(flag & O_CREAT)) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		error = path_lookup_open(dfd, pathname, (flag),
					 &nd, flag);
		if (error)
			return ERR_PTR(error);
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		goto ok;
	}

	/*
	 * Create - we need to know the parent.
	 */
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	error = path_init(dfd, pathname, LOOKUP_PARENT, &nd);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	if (error)
		return ERR_PTR(error);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	error = path_walk(pathname, &nd);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	if (error) {
		if (nd.root.mnt)
			// path_put(&nd.root);
		return ERR_PTR(error);
	}
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// if (unlikely(!audit_dummy_context()))
		// audit_inode(pathname, nd.path.dentry);

	/*
	 * We have the parent and last component. First of all, check
	 * that we are not asked to creat(2) an obvious directory - that
	 * will not do.
	 */
	error = -EISDIR;
	if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
		goto exit_parent;

	printf("this is %s(): %d\r\n", __func__, __LINE__);
	error = -ENFILE;
	filp = get_empty_filp();
	if (filp == NULL)
		goto exit_parent;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	nd.intent.open.file = filp;
	nd.intent.open.flags = flag;
	nd.intent.open.create_mode = mode;
	dir = nd.path.dentry;
	nd.flags &= ~LOOKUP_PARENT;
	nd.flags |= LOOKUP_CREATE | LOOKUP_OPEN;
	// if (flag & O_EXCL)
		// nd.flags |= LOOKUP_EXCL;
	// mutex_lock(&dir->d_inode->i_mutex);
	path.dentry = lookup_hash(&nd);
	path.mnt = nd.path.mnt;

do_last:
	// error = PTR_ERR(path.dentry);
	// if (IS_ERR(path.dentry)) {
	// 	mutex_unlock(&dir->d_inode->i_mutex);
	// 	goto exit;
	// }

	// if (IS_ERR(nd.intent.open.file)) {
	// 	error = PTR_ERR(nd.intent.open.file);
	// 	goto exit_mutex_unlock;
	// }

	/* Negative dentry, just create the file */
	if (!path.dentry->d_inode) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		/*
		 * This write is needed to ensure that a
		 * ro->rw transition does not occur between
		 * the time when the file is created and when
		 * a permanent write count is taken through
		 * the 'struct file' in nameidata_to_filp().
		 */
		// error = mnt_want_write(nd.path.mnt);
		// if (error)
		// 	goto exit_mutex_unlock;
		error = __open_namei_create(&nd, &path, flag, mode);
		if (error) {
			printf("this is %s(): %d\r\n", __func__, __LINE__);
			// mnt_drop_write(nd.path.mnt);
			goto exit;
		}
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		filp = nameidata_to_filp(&nd, open_flag);
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		// if (IS_ERR(filp))
		// 	ima_counts_put(&nd.path,
		// 		       acc_mode & (MAY_READ | MAY_WRITE |
		// 				   MAY_EXEC));
		// mnt_drop_write(nd.path.mnt);
		// if (nd.root.mnt)
			// path_put(&nd.root);
		return filp;
	}

	/*
	 * It already exists.
	 */
	// mutex_unlock(&dir->d_inode->i_mutex);
	// audit_inode(pathname, path.dentry);

	// error = -EEXIST;
	// if (flag & O_EXCL)
	// 	goto exit_dput;

	// if (__follow_mount(&path)) {
	// 	error = -ELOOP;
	// 	if (flag & O_NOFOLLOW)
	// 		goto exit_dput;
	// }

	// error = -ENOENT;
	// if (!path.dentry->d_inode)
	// 	goto exit_dput;
	// if (path.dentry->d_inode->i_op->follow_link)
	// 	goto do_link;

	// path_to_nameidata(&path, &nd);
	// error = -EISDIR;
	// if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
	// 	goto exit;
ok:
	/*
	 * Consider:
	 * 1. may_open() truncates a file
	 * 2. a rw->ro mount transition occurs
	 * 3. nameidata_to_filp() fails due to
	 *    the ro mount.
	 * That would be inconsistent, and should
	 * be avoided. Taking this mnt write here
	 * ensures that (2) can not occur.
	 */
	// will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);
	// if (will_write) {
	// 	error = mnt_want_write(nd.path.mnt);
	// 	if (error)
	// 		goto exit;
	// }
	// error = may_open(&nd.path, acc_mode, flag);
	// if (error) {
	// 	if (will_write)
	// 		mnt_drop_write(nd.path.mnt);
	// 	goto exit;
	// }
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	filp = nameidata_to_filp(&nd, open_flag);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// if (IS_ERR(filp))
	// 	ima_counts_put(&nd.path,
	// 		       acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
	/*
	 * It is now safe to drop the mnt write
	 * because the filp has had a write taken
	 * on its behalf.
	 */
	// if (will_write)
	// 	mnt_drop_write(nd.path.mnt);
	// if (nd.root.mnt)
	// 	path_put(&nd.root);
	return filp;

// exit_mutex_unlock:
// 	mutex_unlock(&dir->d_inode->i_mutex);
// exit_dput:
// 	path_put_conditional(&path, &nd);
exit:
// 	if (!IS_ERR(nd.intent.open.file))
// 		release_open_intent(&nd);
exit_parent:
// 	if (nd.root.mnt)
// 		path_put(&nd.root);
// 	path_put(&nd.path);
	return ERR_PTR(error);

// do_link:
// 	error = -ELOOP;
// 	if (flag & O_NOFOLLOW)
// 		goto exit_dput;
// 	/*
// 	 * This is subtle. Instead of calling do_follow_link() we do the
// 	 * thing by hands. The reason is that this way we have zero link_count
// 	 * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
// 	 * After that we have the parent and last component, i.e.
// 	 * we are in the same situation as after the first path_walk().
// 	 * Well, almost - if the last component is normal we get its copy
// 	 * stored in nd->last.name and we will have to putname() it when we
// 	 * are done. Procfs-like symlinks just set LAST_BIND.
// 	 */
// 	nd.flags |= LOOKUP_PARENT;
// 	error = security_inode_follow_link(path.dentry, &nd);
// 	if (error)
// 		goto exit_dput;
// 	error = __do_follow_link(&path, &nd);
// 	if (error) {
// 		/* Does someone understand code flow here? Or it is only
// 		 * me so stupid? Anathema to whoever designed this non-sense
// 		 * with "intent.open".
// 		 */
// 		release_open_intent(&nd);
// 		if (nd.root.mnt)
// 			path_put(&nd.root);
// 		return ERR_PTR(error);
// 	}
// 	nd.flags &= ~LOOKUP_PARENT;
// 	if (nd.last_type == LAST_BIND)
// 		goto ok;
// 	error = -EISDIR;
// 	if (nd.last_type != LAST_NORM)
// 		goto exit;
// 	if (nd.last.name[nd.last.len]) {
// 		__putname(nd.last.name);
// 		goto exit;
// 	}
// 	error = -ELOOP;
// 	if (count++==32) {
// 		__putname(nd.last.name);
// 		goto exit;
// 	}
// 	dir = nd.path.dentry;
// 	mutex_lock(&dir->d_inode->i_mutex);
// 	path.dentry = lookup_hash(&nd);
// 	path.mnt = nd.path.mnt;
// 	__putname(nd.last.name);
// 	goto do_last;
}

static int user_path_parent(int dfd, const char *path, struct nameidata *nd, char **name)
{
	char *s = getname(path);
	int error;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (IS_ERR(s))
		return PTR_ERR(s);
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd);
	if (error)
		// putname(s);
		printf("this is %s(): %d\r\n", __func__, __LINE__);
	else
		*name = s;

	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return error;
}

struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
	struct dentry *dentry = ERR_PTR(-EEXIST);

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	/*
	 * Yucky last component or no last component at all?
	 * (foo/., foo/.., /////)
	 */
	if (nd->last_type != LAST_NORM)
		goto fail;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	nd->flags &= ~LOOKUP_PARENT;
	nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL;
	nd->intent.open.flags = O_EXCL;

	/*
	 * Do the final lookup.
	 */
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	dentry = lookup_hash(nd);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	if (IS_ERR(dentry))
		goto fail;
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	if (dentry->d_inode)
		goto eexist;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	/*
	 * Special case - lookup gave negative, but... we had foo/bar/
	 * From the vfs_mknod() POV we just have a negative dentry -
	 * all is fine. Let's be bastards - you had / on the end, you've
	 * been asking for (non-existent) directory. -ENOENT for you.
	 */
	if ((!is_dir && nd->last.name[nd->last.len])) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		// dput(dentry);
		dentry = ERR_PTR(-ENOENT);
	}
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return dentry;
eexist:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	// dput(dentry);
	dentry = ERR_PTR(-EEXIST);
fail:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return dentry;
}

int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
	int error = 0;
	// int error = may_create(dir, dentry);

	if (error)
		return error;

	if (!dir->i_op->mkdir)
		return -EPERM;

	// mode &= (S_IRWXUGO|S_ISVTX);
	// error = security_inode_mkdir(dir, dentry, mode);
	// if (error)
	// 	return error;

	// vfs_dq_init(dir);
	error = dir->i_op->mkdir(dir, dentry, mode);
	// if (!error)
		// fsnotify_mkdir(dir, dentry);
	return error;
}

int sys_mkdirat(int dfd, const char *pathname, int mode)
{
	int error = 0;
	char * tmp;
	struct dentry *dentry;
	struct nameidata nd;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	error = user_path_parent(dfd, pathname, &nd, &tmp);
	if (error)
		goto out_err;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	dentry = lookup_create(&nd, 1);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	error = PTR_ERR(dentry);
	if (IS_ERR(dentry))
		goto out_unlock;

	// if (!IS_POSIXACL(nd.path.dentry->d_inode))
	// 	mode &= ~current_umask();
	// error = mnt_want_write(nd.path.mnt);
	// if (error)
	// 	goto out_dput;
	// error = security_path_mkdir(&nd.path, dentry, mode);
	// if (error)
	// 	goto out_drop_write;
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
out_drop_write:
	// mnt_drop_write(nd.path.mnt);
out_dput:
	// dput(dentry);
out_unlock:
	// mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	// path_put(&nd.path);
	// putname(tmp);
out_err:
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return error;
}

int sys_mkdir(const char *pathname, int mode)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	return sys_mkdirat(AT_FDCWD, pathname, mode);
}
